```latex
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du.                                       %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}

\input{\commonfolder/header}
\input{\commonfolder/copyright}

\newcommand{\dnsFigs}{./Figs}

\lhead{\bfseries SEED Labs -- 远程DNS缓存投毒攻击实验室}

\def \code#1 {\fbox{\scriptsize{\texttt{#1}}}}

\begin{document}

\begin{center}
{\LARGE 邦尼斯基攻击实验}
\end{center}

\seedlabcopyright{2006 - 2020}


% *******************************************
% SECTION
% ******************************************* 
\section{实验概述}

本实验的目标是使学生获得远程DNS缓存投毒攻击（也称为邦尼斯基DNS攻击）的第一手经验。DNS（域名系统）是互联网的电话簿；它将主机名翻译为IP地址，反之亦然。这种转换通过后台进行的DNS解析完成。DNS攻击会以各种方式操纵这个解析过程，以误导用户访问替代的目的地，这些目的地往往是恶意的。本实验专注于一种特定的DNS攻击技术，即{\em DNS缓存投毒攻击}。
在另一项SEED实验室活动中，我们设计了活动来在同一网络环境中实施同样的攻击（即攻击者和受害者的DNS服务器在同一网络上，允许嗅探数据包）。在这个远程攻击实验室中，不允许进行数据包嗅探，因此该攻击比本地攻击更具挑战性。本实验包括以下主题：

\begin{itemize}[noitemsep]
    \item DNS及其工作原理
    \item DNS服务器的设置
    \item DNS缓存投毒攻击
    \item 伪造DNS响应
    \item 数据包伪造
\end{itemize}

\paragraph{参考材料和视频。}
关于DNS协议和攻击的详细内容可以在以下资料中找到：

\begin{itemize}
    \item 《SEED书籍》第18章，\seedbook
    \item 《SEED讲义》第7节，\seedisvideo
\end{itemize}

\paragraph{实验环境。} \seedenvironmentC

% Temporarily remove this part, to make the task a little bit simpler
% during the transition phase (from VM to container)
\begin{comment}
\vspace{0.2in}
\noindent
\fbox{\parbox{\textwidth}{
\noindent
\textbf{定制化。}
在本实验描述中，我们使用域名 \texttt{attacker32.com} 来指代攻击者控制的域名。当学生进行此实验时，他们不得使用该名称；应使用包含其姓氏的域名。这项要求的目的是区分学生的成果。由于域名仅在实验室环境中可见，并不对外公开，因此任何名字（包括已被其他人拥有的）都可以安全地用于本实验。
}}
\end{comment}

% *******************************************
% SECTION
% ******************************************* 
\section{实验环境设置（任务1）}
\label{sec:environment}

\begin{figure}[htb]
\centering
\includegraphics[width=0.85\textwidth]{\commonfolder/Figs/DNS.pdf}
\caption{实验的环境设置}
\label{dns:fig:environment}
\end{figure}

DNS缓存投毒攻击的主要目标是本地DNS服务器。显然，攻击真正的服务器是非法的，因此我们需要设置自己的DNS服务器来进行攻击实验。实验室环境需要四台独立的机器：一台作为受害者，一台作为DNS服务器，两台作为攻击者。
该实验环境的设置如图~\ref{dns:fig:environment}所示。

% \begin{lstlisting}[backgroundcolor=]
% +------------+   +------------+  +------------+  +---------------+
% | Attack VM  |   | Container (user) |  | Local DNS Server |  | Attacker32.com |
% |            |   |                    |  |                |  |              |
% | 10.9.0.1   |   | 10.9.0.5    |  |  10.9.0.53   |  | 10.9.0.153  |
% +-----+------+   +-----+------+  +-----+------+  +--------+------+
%       |                 |               |               |
%       |                 |               |               |
% -------+-----------------+---------------+---------------+-------
%             Network: 10.9.0.0/24
%
%\end{lstlisting}

所有这些机器都放置在同一局域网只是为了简化操作。学生不允许利用这一点进行攻击；他们应将攻击者机器视为远程机器，即攻击者不能嗅探局域网中的数据包。
这与本地DNS攻击不同。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器设置和命令}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{关于攻击者容器}

在本实验中，我们可以使用虚拟机或攻击者的容器作为攻击机器。如果你查看Docker Compose文件，会发现攻击者容器的配置与其他容器不同。

\begin{itemize}
    \item \textit{共享文件夹}。当我们使用攻击者容器进行攻击时，需要将攻击代码放在攻击者容器中。
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/volumes}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    \item \textit{主机模式}。
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/host_mode}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{itemize}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/DNS/summary_of_config}
\input{\commonfolder/DNS/setup_testing}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% *******************************************
% SECTION
% ******************************************* 
\section{攻击任务}

DNS攻击的主要目标是在用户试图通过主机名访问机器A时，将其重定向到另一台机器B。例如，假设 \texttt{www.example.com} 是一个在线银行网站。当用户使用正确的URL \texttt{www.example.com} 访问该站点时，如果对手能够将用户重定向到看起来像 \texttt{www.example.com} 的恶意网站，用户可能会被骗并泄露其凭证给攻击者。

在这个任务中，我们将域名 \texttt{www.example.com} 作为我们的攻击目标。需要注意的是，\texttt{example.com} 域名保留用于文档使用，并未用于任何真实公司。该域名的真实IP地址为 \texttt{93.184.216.34}，其名称服务器由互联网号码分配机构（ICANN）管理。当用户运行 \texttt{dig} 命令或在浏览器中输入该名称时，用户的计算机将向本地DNS服务器发送DNS查询请求，最终会向 \texttt{example.com} 的名称服务器请求IP地址。

攻击的目标是在本地DNS服务器上启动DNS缓存投毒攻击，使得当用户运行 \texttt{dig} 命令来查找 \texttt{www.example.com} 的IP地址时，本地DNS服务器将最终访问攻击者的名称服务器 \texttt{ns.attacker32.com} 来获取IP地址，因此返回的IP地址可以由攻击者决定。结果，用户会被引导到攻击者的网站，而不是真正的 \texttt{www.example.com}。

\begin{figure}[htb]
\centering
\includegraphics[width=0.9\textwidth]{\dnsFigs/DNS_Remote_new1.pdf}
\caption{完整的DNS查询过程}
\label{fig:flow_diagram1}
\end{figure}

\begin{figure}[htb]
\centering
\includegraphics[width=0.9\textwidth]{\dnsFigs/DNS_Remote_new2.pdf}
\caption{邦尼斯基攻击}
\label{fig:flow_diagram2}
\end{figure}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{邦尼斯基攻击的运作方式}

在这个任务中，攻击者向受害者的DNS服务器（\texttt{Apollo}）发送DNS查询请求，触发一次DNS查询。该查询可能会经过根DNS服务器、\texttt{.COM} DNS服务器，并最终从 \texttt{example.com} 的名称服务器获得结果。如图~\ref{fig:flow_diagram1}所示。如果 \texttt{example.com} 的名称服务器信息已经缓存在 \texttt{Apollo} 中，查询将不会经过根或 \texttt{.COM} 服务器；如图~\ref{fig:flow_diagram2}所示。在本实验中，图~\ref{fig:flow_diagram2}的情况更为常见，因此我们将以该图为基础描述攻击机制。

当 \texttt{Apollo} 正在等待从 \texttt{example.com} 名称服务器返回的DNS回复时，攻击者可以向 \texttt{Apollo} 发送伪造回复，假装这些回复来自 \texttt{example.com} 的名称服务器。如果伪造回复先到达，则将被 \texttt{Apollo} 接受。攻击将会成功。

如果你已经完成了本地DNS攻击实验室，你应该意识到这些攻击假设攻击者和DNS服务器在同一局域网内，即攻击者可以观察到DNS查询消息。当攻击者和DNS服务器不在同一局域网时，缓存投毒攻击变得更加困难。
这种困难主要是由于DNS响应包中的事务ID必须与查询包中的事务ID匹配造成的。因为查询中的事务ID通常是由随机生成的，没有看到查询包的话，很难知道正确的ID。

显然，攻击者可以猜测事务ID。由于ID的大小仅为16位，如果攻击者在合法回复到达之前能够伪造K个响应，则成功概率为 $K/2^{16}$。发送数百个伪造响应是不切实际的，因此不会花费太多尝试就能取得成功。

然而，上述假设攻击忽略了缓存效果。实际上，如果攻击者未能在真实响应包到达前做出正确的猜测，正确信息将会被DNS服务器缓存一段时间。这种缓存效果使得攻击者无法再为同一个名称伪造另一个响应，因为DNS服务器在缓存超时之前不会再次为此名称发送DNS查询。
要为同一名称伪造另一个响应，攻击者必须等待另一个对该名称的DNS查询，这意味着他/她必须等待缓存超时。等待期可能是几个小时或几天。

\paragraph{邦尼斯基攻击。} Dan Kaminsky 提出了一种巧妙的技术来克服缓存效果~\cite{dns:Kaminsky}。
通过邦尼斯基攻击，攻击者可以连续对域名的DNS服务器发起攻击而无需等待，因此攻击可以在很短的时间内成功。
攻击细节在~\cite{dns:Kaminsky,seedbook} 中有描述。在这个任务中，我们将尝试这种方法。以下步骤与图~\ref{fig:flow_diagram2}相对应概述了攻击过程。

\begin{enumerate}
    \item 攻击者查询DNS服务器 \texttt{Apollo} 以获取在 \texttt{example.com} 中不存在的名称（例如，\texttt{twysw.example.com}），其中 \texttt{twysw} 是一个随机名称。
    
    \item 因为该映射未缓存在 \texttt{Apollo} 的DNS缓存中，所以 \texttt{Apollo} 向 \texttt{example.com} 域的名称服务器发送DNS查询。
    
    \item 当 \texttt{Apollo} 正在等待回复时，攻击者向 \texttt{Apollo} 洪泛以伪造DNS响应，每条尝试一个不同的事务ID，希望其中一个是正确的。在回复中，不仅提供了一个对 \texttt{twysw.example.com} 的IP解析，还提供了“权威名称服务器”记录，将 \texttt{ns.attacker32.com} 作为 \texttt{example.com} 域的名称服务器。
    如果伪造响应击败了实际回复并且事务ID与查询中的匹配，则 \texttt{Apollo} 将接受并缓存伪造的答案，从而 \texttt{Apollo} 的DNS缓存被投毒。 
    
    \item 即使伪造的DNS响应失败（例如，事务ID不匹配或太晚到达），这也没有关系，因为下次攻击者将查询不同的名称，使得 \texttt{Apollo} 必须发送另一个查询给该名称，从而给攻击提供了再次进行伪造攻击的机会。这有效地克服了缓存效果。
    
    \item 如果攻击成功，在 \texttt{Apollo} 的DNS缓存中，\texttt{example.com} 的名称服务器将被攻击者的名称服务器 \texttt{ns.attacker32.com} 替换。
    为了展示这次攻击的成功，学生需要证明这种记录在 \texttt{Apollo} 的DNS缓存中。
    
\end{enumerate}

\paragraph{任务概述。} 实施邦尼斯基攻击相当具有挑战性，所以我们将其分解为几个子任务。在任务2中，我们构建了对 \texttt{example.com} 域随机主机名的DNS请求。在任务3中，我们构建了一个来自 \texttt{example.com} 名称服务器的伪造DNS回复。在任务4中，我们将所有这些组合起来发起邦尼斯基攻击。最后，在任务5中，我们验证了攻击的影响。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务2：构造DNS请求}

此任务的重点是发送DNS请求。为了完成攻击，攻击者需要触发目标DNS服务器发送DNS查询，从而有机会伪造DNS回复。由于攻击者在成功之前需要尝试很多次，最好使用程序来自动化这个过程。

学生需要编写一个程序向目标DNS服务器（即我们在设置中的本地DNS服务器）发送DNS查询。
学生的任务是编写此程序，并通过Wireshark演示其查询可以触发目标DNS服务器发出相应的DNS查询。对于此任务的要求不高，因此学生可以使用C或Python（使用Scapy）来编写此代码。以下是一个Python代码片段（“+++”是占位符；学生需要将其替换为实际值）：

\begin{lstlisting}
Qdsec  = DNSQR(qname='www.example.com')
dns    = DNS(id=0xAAAA, qr=0, qdcount=1, ancount=0, nscount=0,
             arcount=0, qd=Qdsec)

ip  = IP(dst='+++', src='+++')
udp = UDP(dport=+++, sport=+++, chksum=0)
request = ip/udp/dns
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务3：伪造DNS回复。} 

在这个任务中，我们需要在邦尼斯基攻击中伪造DNS响应。由于我们的目标是 \texttt{example.com}，因此需要伪造来自该域名称服务器的响应。学生首先需要找到 \texttt{example.com} 的合法名称服务器的IP地址（应该注意到这个域有多个名称服务器）。

学生可以使用Scapy来实现此任务。以下代码片段构建了一个包含问题部分、答案部分和NS部分的DNS响应包。
在示例代码中，我们使用“+++”作为占位符；学生需要将其替换为邦尼斯基攻击所需的正确值。学生需要解释为什么选择了这些值。

\begin{lstlisting}
name   = '+++'  
domain = '+++'  
ns     = '+++'

Qdsec  = DNSQR(qname=name)
Anssec = DNSRR(rrname=name, type='A', rdata='1.2.3.4', ttl=259200)
NSsec  = DNSRR(rrname=domain, type='NS', rdata=ns, ttl=259200)
dns    = DNS(id=0xAAAA, aa=1, rd=1, qr=1,
             qdcount=1, ancount=1, nscount=1, arcount=0,
             qd=Qdsec, an=Anssec, ns=NSsec)

ip    = IP(dst='+++', src='+++')
udp   = UDP(dport=+++, sport=+++, chksum=0)
reply = ip/udp/dns
\end{lstlisting}

由于这个单独的回复本身不足以导致成功攻击，为了展示此任务，学生需要使用Wireshark捕获伪造的DNS响应，并证明这些伪造的数据包是有效的。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务4：发起邦尼斯基攻击} 

现在我们可以将所有内容组合起来执行邦尼斯基攻击。在攻击中，我们需要发送许多伪造的DNS回复，希望其中一个命中正确的事务号并且比合法回复先到达。因此速度至关重要：我们发送的包越多，成功的机会就越高。
如果我们使用Scapy像上一个任务那样发送伪造的DNS回复，成功率会太低。学生可以使用C，但在C中构造DNS数据包并不容易。
我们将介绍一种混合方法（见《SEED书籍》）使用Scapy和C。

通过这种混合方法，我们首先使用Scapy生成一个DNS数据包模板，并将其保存在一个文件中。然后将这个模板加载到一个C程序中，在某些字段上进行小修改后发送出去。 
我们已经包括了一个示例的C代码在 \path{Labsetup/Files/attack.c} 中。学生可以在标记区域做出更改。
关于代码的详细说明见指导部分。

\paragraph{检查DNS缓存。}
要确定攻击是否成功，我们需要查看 \texttt{dump.db} 文件以确定我们的伪造DNS响应是否被DNS服务器成功接受了。以下命令导出DNS缓存，并在其中搜索包含“attacker”一词（在我们的攻击中我们使用了 \texttt{attacker32.com} 作为攻击者的域名；如果学生使用不同的域名，则应搜索不同的词）。

\begin{lstlisting}
# rndc dumpdb -cache && grep attacker /var/cache/bind/dump.db
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务5：结果验证}

如果攻击成功，在本地DNS服务器的DNS缓存中，\texttt{example.com} 的 \texttt{NS} 记录将变为 \texttt{ns.attacker32.com}。
当这个服务器收到任何主机名在 \texttt{example.com} 域内的DNS查询时，它将向 \texttt{ns.attacker32.com} 发送一个查询，而不是发送到该域的合法名称服务器。

为了验证你的攻击是否成功，前往用户机器并运行以下两个 \texttt{dig} 命令。在响应中，对于两个命令来说 \texttt{www.example.com} 的IP地址应该相同，并且它应该是你攻击者名称服务器上的区域文件中包含的任何内容。

\begin{lstlisting}
// 请求本地DNS服务器进行查询
$ dig www.example.com

// 直接询问攻击者的32名称服务器
$ dig @ns.attacker32.com www.example.com
\end{lstlisting}

请在实验报告中包括你的观察结果（截图），并解释你认为为什么你的攻击是成功的。特别是当你运行第一个 \texttt{dig} 命令时，请使用Wireshark捕获网络流量，并指出这个 \texttt{dig} 命令触发了什么数据包。使用数据包跟踪来证明你的攻击是成功的。注意，DNS结果可能在你第一次运行 \texttt{dig} 命令后被本地DNS服务器缓存。
如果你在使用Wireshark之前运行了第一个 \texttt{dig} 命令，这可能会对结果产生影响。你可以通过在本地DNS服务器上执行“sudo rndc flush”清除缓存，但这将需要你重新进行攻击。

% *******************************************
% SECTION
% ******************************************* 
\section{指导方针}

为了实现邦尼斯基攻击，我们可以使用Scapy来进行数据包伪造。不幸的是，
Python的速度太慢；每秒生成的包数太少，无法使攻击成功。最好使用C程序。
这可能对许多学生来说是一个挑战，因为用C构造DNS数据包并不容易。我已经开发了一种混合方法，并在我的课堂上进行了实验。通过这种方法，学生的编码时间可以显著减少，从而他们可以将更多的时间专注于实际的攻击。

这个想法是利用Scapy和C的优点：Scapy比C更方便于创建DNS数据包，但C的速度更快。因此我们简单地使用Scapy生成伪造的DNS数据包，并将其保存在一个文件中。
然后我们将数据包加载到一个C程序中。即使在邦尼斯基攻击过程中需要发送许多不同的DNS数据包，
这些数据包大部分相同，只是少数几个字段有变化。因此，我们可以
以Scapy生成的数据包为基础，找到需要修改的位置（例如事务ID字段），并直接进行修改。
这比完全使用C创建整个DNS数据包要容易得多。
在进行了更改之后，可以使用原始套接字发送这些数据包。
关于这种混合方法的详细内容见《SEED书籍》中的嗅探和伪造章节~\cite{seedbook}。

以下是一个Scapy程序用于生成一个简单的DNS回复数据包，并将其保存到文件中。

\begin{lstlisting}[caption={\texttt{generate\_dns\_reply.py}}]
#!/usr/bin/env python3
from scapy.all import *

# 构造DNS头和负载
name   = 'twysw.example.com'
Qdsec  = DNSQR(qname=name)
Anssec = DNSRR(rrname=name, type='A', rdata='1.1.2.2', ttl=259200)
dns    = DNS(id=0xAAAA, aa=1, rd=0, qr=1,
             qdcount=1, ancount=1, nscount=0, arcount=0, 
             qd=Qdsec, an=Anssec)

# 构造IP和UDP头以及整个数据包
ip  = IP(dst='10.0.2.7', src='1.2.3.4', chksum=0)
udp = UDP(dport=33333, sport=53, chksum=0)
pkt = ip/udp/dns

# 将数据包保存到文件
with open('ip.bin', 'wb') as f:
  f.write(bytes(pkt))
\end{lstlisting}

在C程序中，我们从文件 \texttt{ip.bin} 中加载该数据包，并使用它作为我们的数据包模板，在此基础上创建许多类似的数据包，并将这些伪造回复洪泛到目标本地DNS服务器。对于每个回复，我们需要更改三个地方：事务ID以及出现在两个位置的名称\texttt{twysw}（问题部分和答案部分）。事务ID固定在IP数据包的第28个字节处（从开始算起），但名称 \texttt{twysw} 的偏移量取决于域名的长度。我们可以使用二进制编辑程序，如 \texttt{bless} 来查看二进制文件 \texttt{ip.bin} 并找到两个 \texttt{twysw} 的偏移量。在我们的数据包中，它们位于第41个和64个字节处。

以下代码片段展示了如何对这些字段进行更改。我们将回复中的名称修改为 \texttt{bbbbb.example.com} ，然后发送伪造的DNS回复，并且事务ID为 \texttt{1000} 。在代码中，变量 \texttt{ip} 指向IP数据包的开始。

\begin{lstlisting}
  // 修改问题字段中的名称（偏移=41）
  memcpy(ip+41, "bbbbb" , 5);

  // 修改答案字段中的名称（偏移=64）
  memcpy(ip+64, "bbbbb" , 5);

  // 修改事务ID字段（偏移=28）
  unsigned short id = 1000;
  unsigned short id_net_order = htons(id);
  memcpy(ip+28, &id_net_order, 2);
\end{lstlisting}

\paragraph{生成随机名称。} 在邦尼斯基攻击中，我们需要生成随机主机名。有许多方法可以做到这一点。
以下代码片段展示了如何生成一个长度为5的随机名称。

\begin{lstlisting}
char a[26]="abcdefghijklmnopqrstuvwxyz";

// 生成一个长度为5的随机名称
char name[6];
name[5] = 0;
for (int k=0; k<5; k++)  
   name[k] = a[rand() % 26];
\end{lstlisting}

\paragraph{编译程序。}
要编译该程序，可以使用以下命令：

\begin{lstlisting}
$ gcc -o attack attack.c

// 对Apple Silicon机器：使用静态链接
$ gcc -static -o attack attack.c
\end{lstlisting}

% *******************************************
% SECTION
% ******************************************* 
\section{提交作业}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\thispagestyle{empty}
\bibliographystyle{plain}
\def\baselinestretch{1}
\bibliography{BibDNS}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



\end{document}
```